// Protocol Buffers for GWT
// Copyright 2010 Thomas Broyer  All rights reserved.
// http://code.google.com/p/protobuf-gwt/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package net.ltgt.gwt.protobuf.compiler;

import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.EnumDescriptor;
import com.google.protobuf.Descriptors.FileDescriptor;
import com.google.protobuf.Descriptors.ServiceDescriptor;
import com.google.protobuf.compiler.CodeGenerator;
import com.google.protobuf.compiler.JavaHelpers;
import com.google.protobuf.compiler.Plugin;

import net.ltgt.gwt.protobuf.compiler.client.FileGenerator;

import java.io.IOException;
import java.io.Writer;
import java.util.Locale;
import java.util.Map;

/**
 * @author t.broyer@ltgt.net Thomas Broyer
 * <br>Based on the initial work of:
 * @author kenton@google.com Kenton Varda
 * <br>Based on original Protocol Buffers design by
 * Sanjay Ghemawat, Jeff Dean, and others.
 */
public class Main implements CodeGenerator {

  public static void main(String[] args) {
    Plugin.run(new Main());
  }

  private enum Mode {
    SERVER,
    CLIENT,
    SUPER
  }

  private static Mode parseParameter(String parameter) throws GeneratorException {
    Map<String, String> options = JavaHelpers.parseGeneratorParameter(parameter);
    String mode = options.get("mode");
    if (mode == null) {
      return Mode.SERVER;
    }
    try {
      return Mode.valueOf(Mode.class, mode.toUpperCase(Locale.ENGLISH));
    } catch (IllegalArgumentException iae) {
      throw new GeneratorException("Unrecognized mode value: " + mode, iae);
    }
  }

  @Override
  public void generate(FileDescriptor file, String parameter,
      Context context) throws GeneratorException {
    Mode mode = parseParameter(parameter);
    
    FileGenerator fileGenerator = new FileGenerator(file);

    String packageDir = fileGenerator.getJavaPackage().replace('.', '/');
    if (!packageDir.isEmpty()) {
      packageDir += '/';
    }

    if (mode == Mode.CLIENT || mode == Mode.SUPER) {
      String gwtPackageDir;
      
      if (mode == Mode.CLIENT) {
        // TODO: refactor into FileGenerator
        String moduleFilename = packageDir + fileGenerator.getClassName()
        + ".gwt.xml";
        StringBuilder module = new StringBuilder();
        module.append("<!--\n");
        module.append("Generated by the GWT protocol buffer compiler code generator plugin. DO NOT EDIT.\n");
        module.append("source: " + file.getName() + "\n\n");
        module.append("-->\n");
        module.append("<module>\n");
        module.append("<inherits name='net.ltgt.gwt.protobuf.ProtocolBuffers' />\n");
        module.append("<inherits name='com.google.gwt.user.User' />\n");
        module.append("<super-source path='super' />\n");
        module.append("<source path=''>\n");
        module.append("  <include name='" 
            + fileGenerator.getClassName() + ".java' />\n");
        if (file.getOptions().getJavaMultipleFiles()) {
          for (Descriptor descriptor : file.getMessageTypes()) {
            module.append("  <include name='" + descriptor.getName()
                + ".java' />\n");
            module.append("  <include name='" + descriptor.getName()
                + "_CustomFieldSerializer.java' />\n");
          }
          for (EnumDescriptor enumType : file.getEnumTypes()) {
            module.append("  <include name='" + enumType.getName()
                + ".java' />\n");
          }
          for (ServiceDescriptor service : file.getServices()) {
            module.append("  <include name='" + service.getName()
                + ".java' />\n");
            module.append("  <include name='" + service.getName()
                + "Async.java' />\n");
          }
        }
        module.append("</source>\n");
        context.addFile(moduleFilename,
            module.append("</module>").toString());

        gwtPackageDir = packageDir + "super/" + packageDir;
      } else {
        gwtPackageDir = packageDir;
      }

      String javaFilename = gwtPackageDir + fileGenerator.getClassName()
          + ".java";

      try {
        Writer output = context.open(javaFilename);
        try {
          fileGenerator.generate(output);
        } finally {
          output.close();
        }

        fileGenerator.generateSiblings(gwtPackageDir, context);
      } catch (IOException e) {
        throw new GeneratorException(e);
      }
    }

    if (mode != Mode.SUPER) {
      String javaFilename = packageDir + fileGenerator.getClassName() + ".java";
      try {
        net.ltgt.gwt.protobuf.compiler.server.FileGenerator generator = new net.ltgt.gwt.protobuf.compiler.server.FileGenerator(
            file);
        generator.generate(javaFilename, context);

        generator.generateSiblings(packageDir, context);
      } catch (IOException e) {
        throw new GeneratorException(e);
      }
    }
  }
}
